/* spipc.c */

/**********************/
/* INTER-PROCESS-COMM */
/* for Secure Phone   */
/* Version 1.01,25.12 */
/* (C) 2002 by NAH6   */
/**********************/

/*------------*/
/*  INCLUDES  */
/*------------*/

#include <windows.h>

#include "spipc.h"

/*-----------*/
/*  DEFINES  */
/*-----------*/

#define K_SPIPC_MAXUSERS          (1000)

#define K_SPIPC_QUEUE_NUM            (2)
#define K_SPIPC_QUEUE_LEN        (32742)
#define K_SPIPC_QUEUE_WRTRIG      (8192)

#define K_SPIPC_INITTIME          (5000)
#define K_SPIPC_ATOMICTIME        (1000)
#define K_SPIPC_WRCHKTIME           (50)

/*--------------*/
/*  STRUCTURES  */
/*--------------*/

/* Queue handles */
struct spipc_queue_handle
{
  HANDLE atomic;
  HANDLE notempty;
  HANDLE notfull;
};

/* Queue layout */
struct spipc_queue_data
{
  HWND rxhwnd;
  HWND txhwnd;
  unsigned short rdpos;
  unsigned short wrpos;
  unsigned short msgcnt;
  unsigned char  buffer[K_SPIPC_QUEUE_LEN];
};

/*----*/

/* Shared memory */
struct spipc_shared_mem
{
  SPIPC_VERSIONINFO       version;
  struct spipc_queue_data queue[K_SPIPC_QUEUE_NUM];
};

/*--------------*/
/*  PROTOTYPES  */
/*--------------*/

static signed short spipc__makeptr (unsigned char queue,           \
                                    struct spipc_queue_handle **h, \
                                    struct spipc_queue_data **q);

static signed short spipc__lock   (struct spipc_queue_handle *h);
static signed short spipc__unlock (struct spipc_queue_handle *h);

static unsigned short spipc__queuelen (const struct spipc_queue_data *q);

/*-------------*/
/*  VARIABLEN  */
/*-------------*/

/* Process memory */
static HINSTANCE                 spipc_instance;
static HANDLE                    spipc_initlock;
static HANDLE                    spipc_memfile;
static struct spipc_shared_mem  *spipc_membase;
static struct spipc_queue_handle spipc_queuehnd[K_SPIPC_QUEUE_NUM];

/*----*/

/* Object names */
static const TCHAR spipc__init_atomic[]  = TEXT ("SPIPC_INIT_ATOMIC");
static const TCHAR spipc__mem_shared[]   = TEXT ("SPIPC_MEM_SHARED");
static const TCHAR spipc__cmd_atomic[]   = TEXT ("SPIPC_CMD_ATOMIC");
static const TCHAR spipc__cmd_notempty[] = TEXT ("SPIPC_CMD_NOTEMPTY");
static const TCHAR spipc__cmd_notfull[]  = TEXT ("SPIPC_CMD_NOTFULL");
static const TCHAR spipc__res_atomic[]   = TEXT ("SPIPC_RES_ATOMIC");
static const TCHAR spipc__res_notempty[] = TEXT ("SPIPC_RES_NOTEMPTY");
static const TCHAR spipc__res_notfull[]  = TEXT ("SPIPC_RES_NOTFULL");

/******************************************************************************/

/*-------------*/
/*  WIN-ENTRY  */
/*-------------*/
                                                      /* Windows-Entry [1800] */
BOOL APIENTRY DllMain (HINSTANCE inst, DWORD reason, LPVOID reserved)
{
  HANDLE hnd;
  BOOL b,n;
  DWORD d;

  /* Dll notification */
  if (reason == DLL_PROCESS_ATTACH)
  {
    /* Dll config */
    n = FALSE;
    b = DisableThreadLibraryCalls ((HMODULE)inst);
    if (!b)
      return FALSE;
    spipc_instance = inst;

    /* Shared-memory view */
    hnd = CreateEvent (NULL, FALSE, TRUE, spipc__init_atomic);
    if (hnd == NULL)
      return FALSE;
    spipc_initlock = hnd;
    d = WaitForSingleObject (hnd, K_SPIPC_INITTIME);
    if (d != WAIT_OBJECT_0)
      goto err_exit11;
    hnd = CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE , \
                             0L, sizeof (struct spipc_shared_mem),        \
                             spipc__mem_shared);
    if (hnd == NULL)
      goto err_exit10;
    spipc_memfile = hnd;
    d = GetLastError ();
    spipc_membase = MapViewOfFile (hnd, FILE_MAP_WRITE, 0L, 0L, \
                                   sizeof (struct spipc_shared_mem));
    if (spipc_membase == NULL)
      goto err_exit9;
    if (d != ERROR_ALREADY_EXISTS)
    {
      /* Init shared memory */
      FillMemory (spipc_membase, sizeof (struct spipc_shared_mem), 0x00);
      SPIPC_getVersionInfo (&(spipc_membase->version));
      spipc_membase->version.creator = GetCurrentProcessId ();
      spipc_membase->queue[K_SPIPC_QUEUE_COMMAND].rxhwnd = NULL;
      spipc_membase->queue[K_SPIPC_QUEUE_COMMAND].txhwnd = NULL;
      spipc_membase->queue[K_SPIPC_QUEUE_RESULT].rxhwnd  = NULL;
      spipc_membase->queue[K_SPIPC_QUEUE_RESULT].txhwnd  = NULL;
    }
    if (spipc_membase->version.dlluse >= K_SPIPC_MAXUSERS)
      goto err_exit8;
    spipc_membase->version.dlluse++;
    b = SetEvent (spipc_initlock);
    if (!b)
      goto err_exit7;

    /* Command-queue handles */
    hnd = CreateEvent (NULL, FALSE, TRUE, spipc__cmd_atomic);
    if (hnd == NULL)
      goto err_exit6;
    spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].atomic = hnd;
    hnd = CreateEvent (NULL, TRUE, FALSE, spipc__cmd_notempty);
    if (hnd == NULL)
      goto err_exit5;
    spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].notempty = hnd;
    hnd = CreateEvent (NULL, TRUE, TRUE, spipc__cmd_notfull);
    if (hnd == NULL)
      goto err_exit4;
    spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].notfull = hnd;

    /* Result-queue handles */
    hnd = CreateEvent (NULL, FALSE, TRUE, spipc__res_atomic);
    if (hnd == NULL)
      goto err_exit3;
    spipc_queuehnd[K_SPIPC_QUEUE_RESULT].atomic = hnd;
    hnd = CreateEvent (NULL, TRUE, FALSE, spipc__res_notempty);
    if (hnd == NULL)
      goto err_exit2;
    spipc_queuehnd[K_SPIPC_QUEUE_RESULT].notempty = hnd;
    hnd = CreateEvent (NULL, TRUE, TRUE, spipc__res_notfull);
    if (hnd == NULL)
      goto err_exit1;
    spipc_queuehnd[K_SPIPC_QUEUE_RESULT].notfull = hnd;
    n = TRUE;
  }
  else if (reason == DLL_PROCESS_DETACH)
  {
    /* Free handles */
    n = TRUE;
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_RESULT].notfull);
    err_exit1:
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_RESULT].notempty);
    err_exit2:
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_RESULT].atomic);
    err_exit3:
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].notfull);
    err_exit4:
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].notempty);
    err_exit5:
    CloseHandle (spipc_queuehnd[K_SPIPC_QUEUE_COMMAND].atomic);
    err_exit6:
    d = WaitForSingleObject (spipc_initlock, K_SPIPC_INITTIME);
    err_exit7:
    spipc_membase->version.dlluse--;
    if (!spipc_membase->version.dlluse)
      FillMemory (spipc_membase, sizeof (struct spipc_shared_mem), 0x00);
    err_exit8:
    UnmapViewOfFile (spipc_membase);
    err_exit9:
    CloseHandle (spipc_memfile);
    if ((n == FALSE) || (d == WAIT_OBJECT_0))
    {
      err_exit10:
      SetEvent (spipc_initlock);
    }
    err_exit11:
    CloseHandle (spipc_initlock);
  }
  return n;
}

/*----------------------------------------------------------------------------*/

/*--------------*/
/*  QUEUE-INFO  */
/*--------------*/
                                                    /* Get DLL-Version [1801] */
SPIPC_PUBLIC int CALLBACK SPIPC_getVersionInfo (SPIPC_VERSIONINFO *info)
{
  /* Check parameter */
  if (info == NULL)
    return E_SPIPC_NULLPTR;

  /* Return version-info */
  info->magic   = K_SPIPC_MAGIC;
  info->ifver   = K_SPIPC_IFVER;
  info->ifrev   = K_SPIPC_IFREV;
  info->ifopt   = K_SPIPC_IFOPT;
  info->queues  = K_SPIPC_QUEUE_NUM;
  info->dlluse  = spipc_membase->version.dlluse;
  info->creator = spipc_membase->version.creator;
  info->code    = (void *)DllMain;
  info->data    = spipc_membase;
  return 0;
}

/*----*/
                                                     /* Get Queue-Info [1802] */
SPIPC_PUBLIC int CALLBACK SPIPC_getQueueInfo (unsigned char queue, \
                                        SPIPC_QUEUEINFO *info)
{
  struct spipc_queue_handle *h;
  struct spipc_queue_data *q;
  signed short n;

  /* Check parameter */
  n = spipc__makeptr (queue, &h ,&q);
  if (n < 0)
    return n;
  if (info == NULL)
    return E_SPIPC_NULLPTR;

  /* Get infos */
  info->bytes_total   = K_SPIPC_QUEUE_LEN;
  info->bytes_trigger = K_SPIPC_QUEUE_WRTRIG;
  info->evt_rxrdy     = h->notempty;
  info->evt_txrdy     = h->notfull;
  n = spipc__lock (h);
  if (n >= 0)
  {
    info->messages   = q->msgcnt;
    info->bytes_used = spipc__queuelen (q);
    info->hwnd_rxrdy = q->rxhwnd;
    info->hwnd_txrdy = q->txhwnd;
    n = spipc__unlock (h);
  }
  return n;
}

/*----*/
                                            /* Set Notofication-Window [1803] */
SPIPC_PUBLIC int CALLBACK SPIPC_setNotifyWnd (unsigned char queue, \
                                        unsigned char notify, HWND hwnd)
{
  struct spipc_queue_handle *h;
  struct spipc_queue_data *q;
  signed short n;

  /* Check parameter */
  n = spipc__makeptr (queue, &h ,&q);
  if (n < 0)
    return n;
  if (notify > K_SPIPC_NOTIFY_TXRDY)
    return E_SPIPC_BADNOTIFY;

  /* Store hwnd */
  n = spipc__lock (h);
  if (n >= 0)
  {
    if (notify == K_SPIPC_NOTIFY_RXRDY)
      q->rxhwnd = hwnd;
    else
      q->txhwnd = hwnd;
    n = spipc__unlock (h);
  }
  return n;
}

/*-------------*/
/*  QUEUE-I/O  */
/*-------------*/
                                               /* Put Message to Queue [1810] */
SPIPC_PUBLIC int CALLBACK SPIPC_putMessage (unsigned char queue,       \
                                      const unsigned char *data, \
                                      unsigned long timeout)
{
  struct spipc_queue_handle *h;
  struct spipc_queue_data *q;
  unsigned short x,y,z;
  signed short n,m;
  unsigned char f;
  DWORD s,d;
  BOOL b;

#ifdef _DEBUG_IPCMESSAGES
  ((SPIPC_HEADER*)data)->time= GetTickCount();
#endif

  /* Check parameter */
  n = spipc__makeptr (queue, &h ,&q);
  if (n < 0)
    return n;
  if (data == NULL)
    return E_SPIPC_NULLPTR;
  x = data[1];
  x = (x << 8) | data[0];
  if (x < 8)
    return E_SPIPC_STRUCT2SMALL;
  if (x > K_SPIPC_QUEUE_LEN)
    return E_SPIPC_STRUCT2BIG;

  /* Write message */
  f = 0;
  s = GetTickCount ();
  while (42)
  {
    /* Try write */
    n = spipc__lock (h);
    if (n < 0)
      break;
    y = spipc__queuelen (q);
    y = K_SPIPC_QUEUE_LEN - y;
    if (x <= y)
    {
      /* Message fits */
      z = K_SPIPC_QUEUE_LEN - q->wrpos;
      if (z > x)
        z = x;
      CopyMemory (q->buffer + q->wrpos, data, z);
      if (x > z)
        CopyMemory (q->buffer, data + z, x - z);
      d = q->wrpos;
      d += x;
      if (d >= K_SPIPC_QUEUE_LEN)
        d -= K_SPIPC_QUEUE_LEN;
      q->wrpos = (unsigned short)d;
      q->msgcnt++;
      b = SetEvent (h->notempty);
      if (!b)
        n = E_SPIPC_EVENTSET;
      if ((y - x) < K_SPIPC_QUEUE_WRTRIG)
      {
        b = ResetEvent (h->notfull);
        if ((!b) && (n >= 0))
          n = E_SPIPC_EVENTCLR;
      }
      if (q->rxhwnd != NULL)
      {
        y = q->msgcnt;
        if (y > 4095)
          y = 4095;
        d = data[5];
        d = (d << 8) | data[4];
        d = (d << 12) | y;
        y = data[3];
        y = (y << 8) | data[2];
        PostMessage (q->rxhwnd, WM_SPIPC_MESSAGE, y, d);
      }
      f = 1;
    }
    m = spipc__unlock (h);
    if ((m < 0) && (n >= 0))
      n = m;
    if ((n < 0) || f)
      break;
    /* Wait ready */
    if (timeout == SPIPC_TIME_NOWAIT)
      goto time_over;
    else if (timeout == SPIPC_TIME_FOREVER)
      d = INFINITE;
    else
    {
      d = GetTickCount ();
      d -= s;
      if (d >= timeout)
      {
        time_over:
        n = E_SPIPC_FULL;
        break;
      }
      d = timeout - d;
      if (d > 0x7FFFFFFFL)
        d = 0x7FFFFFFFL;
    }
    if (x <= K_SPIPC_QUEUE_WRTRIG)
    {
      d = WaitForSingleObject (h->notfull, d);
      if ((d != WAIT_OBJECT_0) && (d != WAIT_TIMEOUT))
      {
        n = E_SPIPC_EVENTWAIT;
        break;
      }
    }
    else
    {
      if (d > K_SPIPC_WRCHKTIME)
        d = K_SPIPC_WRCHKTIME;
      Sleep (d);
    }
  }
  return n;
}

/*----*/
                                             /* Get Message from Queue [1811] */
SPIPC_PUBLIC int CALLBACK SPIPC_getMessage (unsigned char   queue, \
                                      unsigned char  *data,  \
                                      unsigned short *size,  \
                                      unsigned long   timeout)
{
  struct spipc_queue_handle *h;
  struct spipc_queue_data *q;
  unsigned short x,y,z;
  signed short n,m;
  unsigned char f;
  DWORD s,d;
  BOOL b;

  /* Check parameter */
  n = spipc__makeptr (queue, &h ,&q);
  if (n < 0)
    return n;
  if (size == NULL)
    return E_SPIPC_NULLPTR;

  /* Read message */
  f = 0;
  s = GetTickCount ();
  while (42)
  {
    /* Try read */
    n = spipc__lock (h);
    if (n < 0)
      break;
    if (q->msgcnt)
    {
      /* Message found */
      y = q->rdpos + 1;
      if (y >= K_SPIPC_QUEUE_LEN)
        y = 0;
      x = q->buffer[y];
      x = (x << 8) | q->buffer[q->rdpos];
      y = spipc__queuelen (q);
      z = *size;
      *size = x;
      if ((x < 8) | (x > y))
        n = E_SPIPC_BADBUFFER;
      else if (data != NULL)
      {
        /* Copy message */
        if (x > z)
          n = E_SPIPC_STRUCT2SMALL;
        else
        {
          z = K_SPIPC_QUEUE_LEN - q->rdpos;
          if (z > x)
            z = x;
          CopyMemory (data, q->buffer + q->rdpos, z);
#ifndef _DEBUG_IPCMESSAGES
          FillMemory (q->buffer + q->rdpos, z, 0x00);
#endif
          if (x > z)
          {
            CopyMemory (data + z, q->buffer, x - z);
#ifndef _DEBUG_IPCMESSAGES
            FillMemory (q->buffer, x - z, 0x00);
#endif
          }
          d = q->rdpos;
          d += x;
          if (d >= K_SPIPC_QUEUE_LEN)
            d -= K_SPIPC_QUEUE_LEN;
          q->rdpos = (unsigned short)d;
          q->msgcnt--;
          if (!(q->msgcnt))
          {
            b = ResetEvent (h->notempty);
            if (!b)
              n = E_SPIPC_EVENTCLR;
          }
          x = K_SPIPC_QUEUE_LEN - y + x;
          if (x >= K_SPIPC_QUEUE_WRTRIG)
          {
            b = SetEvent (h->notfull);
            if ((!b) && (n >= 0))
              n = E_SPIPC_EVENTSET;
          }
          if (q->txhwnd != NULL)
          {
            d = x;
            x = q->msgcnt;
            if (x > 4095)
              x = 4095;
            d = (d << 12) | x;
            PostMessage (q->txhwnd, WM_SPIPC_MESSAGE, SPIPC_DUMMY_TXRDY, d);
          }
        }
      }
      f = 1;
    }
    m = spipc__unlock (h);
    if ((m < 0) && (n >= 0))
      n = m;
    if ((n < 0) || f)
      break;
    /* Wait ready */
    if (timeout == SPIPC_TIME_NOWAIT)
      goto time_over;
    else if (timeout == SPIPC_TIME_FOREVER)
      d = INFINITE;
    else
    {
      d = GetTickCount ();
      d -= s;
      if (d >= timeout)
      {
        time_over:
        n = E_SPIPC_EMPTY;
        break;
      }
      d = timeout - d;
      if (d > 0x7FFFFFFFL)
        d = 0x7FFFFFFFL;
    }
    d = WaitForSingleObject (h->notempty, d);
    if ((d != WAIT_OBJECT_0) && (d != WAIT_TIMEOUT))
    {
      n = E_SPIPC_EVENTWAIT;
      break;
    }
  }
  return n;
}

/*----------------------------------------------------------------------------*/

/*----------------*/
/*  QUEUE-BASICS  */
/*----------------*/
                                                            /* Make Queue-Ptr */
static signed short spipc__makeptr (unsigned char queue,           \
                                    struct spipc_queue_handle **h, \
                                    struct spipc_queue_data **q)
{
  /* Check parameter */
  if (queue >= K_SPIPC_QUEUE_NUM)
    return E_SPIPC_BADQUEUE;

  /* Calc pointer */
  *h = &(spipc_queuehnd[queue]);
  *q = &(spipc_membase->queue[queue]);
  return 0;
}

/*----*/

static signed short spipc__lock (struct spipc_queue_handle *h)  /* Lock Queue */
{
  signed short n;
  DWORD d;

  /* Start atomic update */
  d = WaitForSingleObject (h->atomic, K_SPIPC_ATOMICTIME);
  if (d == WAIT_OBJECT_0)
    n = 0;
  else if (d == WAIT_TIMEOUT)
    n = E_SPIPC_ATOMICTIME;
  else
    n = E_SPIPC_ATOMICWAIT;
  return n;
}

                                                              /* Unlock Queue */
static signed short spipc__unlock (struct spipc_queue_handle *h)
{
  signed short n;
  BOOL b;

  /* End atomic update */
  b = SetEvent (h->atomic);
  if (b)
    n = 0;
  else
    n = E_SPIPC_ATOMICFREE;
  return n;
}

/*----*/
                                                             /* Get Queue-Len */
static unsigned short spipc__queuelen (const struct spipc_queue_data *q)
{
  unsigned short x;

  /* Calc used length */
  if (!(q->msgcnt))
    x = 0;
  else
  {
    x = q->wrpos - q->rdpos;
    if (q->wrpos <= q->rdpos);
      x += K_SPIPC_QUEUE_LEN;
  }
  return x;
}


/*** EOF ***/
